package cn.clj1.builtInFunctions;

import cn.clj1.cell.CljCellType;
import cn.clj1.cell.CljCell;
import cn.clj1.func.CljFunc;
import cn.clj1.func.CljFuncParam;
import cn.clj1.func.MyCljFunc;
import cn.clj1.recur.CljRecur;
import cn.clj1.MyClojureInterp;
import com.sun.source.tree.BreakTree;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;

/**
 * 调用函数
 * */
public class CljMyFunc {

    /**
     * 让形参对应实参，返回 Hashtable
     * */
    public static Hashtable<String, CljCell> getParams(List<CljFuncParam> cljFuncParamList, List<CljCell> ps) {
        if (cljFuncParamList != null && cljFuncParamList.isEmpty() == false && ps != null && ps.isEmpty() == false)
        {
            Hashtable<String, CljCell> ht = new Hashtable<String, CljCell>();
            for (int i = 0; i < cljFuncParamList.size(); i++) {
                CljFuncParam m = cljFuncParamList.get(i);
                if (m.getName() != null) {
                    ht.put(m.getName(), ps.get(i));
                } else if (m.getRestParam() != null) {
                    ht.put(m.getRestParam(), new CljCell(CljCellType.list, ps.subList(i, ps.size())));
                } else if (m.getCljFuncParamList() != null && m.getCljFuncParamList().size() == 2) {
                    CljCell cell = ps.get(i);
                    if (cell.getCljCellType() == CljCellType.list || cell.getCljCellType() == CljCellType.bracket) {
                        List<CljCell> pss = (List<CljCell>) cell.getCell();
                        if (pss != null && pss.isEmpty() == false) {
                            ht.put(m.getCljFuncParamList().get(0).getName(), pss.get(0));
                            ht.put(m.getCljFuncParamList().get(1).getName(), new CljCell(CljCellType.list, pss.subList(1, pss.size())));
                        }
                    }
                }
            }
            return ht;
        }
        return null;
    }

    /**
     * 调用方法
     * */
    public static CljCell Clj_list_symbol(List<CljCell> lst, Hashtable<String, CljCell> funcHs, CljRecur cljRecur, CljCell rs) throws Exception {
        CljCell c = lst.get(0);
        // 判断是否存在 func 中
        if (MyClojureInterp.funcs.containsKey(c.getCell().toString())) {
            MyCljFunc myCljFunc = MyClojureInterp.funcs.get(c.getCell().toString());
            // 判断是否是 override
            if (myCljFunc.getOverride()) {
                Integer psNum = lst.size() - 1;
                CljFunc cljFunc = myCljFunc.getCljFunc(psNum);
                if (cljFunc != null) {

                    // 参数
                    List<CljFuncParam> cljFuncParamList = cljFunc.getFuncParams();
                    // 函数体
                    CljCell cljCellBody = cljFunc.getFuncBody();
                    Hashtable<String, CljCell> ht = getParams(cljFuncParamList, lst.subList(1, lst.size()));
                    cljRecur = new CljRecur();
                    cljRecur.setFuncCellBody(cljCellBody);
                    cljRecur.setCljFuncParamList(cljFuncParamList);
                    return MyClojureInterp.eval(cljCellBody, ht, cljRecur, rs);
                }
            } else {
                CljFunc cljFunc = myCljFunc.getCljFunc();
                // 参数
                List<CljFuncParam> cljFuncParamList = cljFunc.getFuncParams();
                // 函数体
                CljCell cljCellBody = cljFunc.getFuncBody();
                Hashtable<String, CljCell> ht = getParams(cljFuncParamList, lst.subList(1, lst.size()));
                cljRecur = new CljRecur();
                cljRecur.setFuncCellBody(cljCellBody);
                cljRecur.setCljFuncParamList(cljFuncParamList);
                return MyClojureInterp.eval(cljCellBody, ht, cljRecur, rs);
            }
        } else if (funcHs != null && funcHs.containsKey(c.getCell().toString())) {
            return funcHs.get(c.getCell().toString());
        } else if (funcHs != null && c.getCljCellType() == CljCellType.percent && funcHs.containsKey(c.getCell().toString())) {
            return funcHs.get(c.getCell().toString());
        } else {
            throw new Exception("unexpected 未定义字符 " + c.getCell().toString());
        }
        return null;
    }

    /**
     * 匿名调用方法
     * */
    public static CljCell Clj_list_fn(List<CljCell> lst, Hashtable<String, CljCell> funcHs, CljRecur cljRecur, CljCell rs) throws Exception {
        if (lst.get(0).getCljCellType() == CljCellType.Clj_list) {
            MyCljFunc myCljFunc = CljFunctions.Clj_list_fn((List<CljCell>)lst.get(0).getCell(), funcHs, cljRecur, rs);//MyClojureInterp.funcs.get(c.getCell().toString());
            // 判断是否是 override
            if (myCljFunc.getOverride()) {
                Integer psNum = lst.size() - 1;
                CljFunc cljFunc = myCljFunc.getCljFunc(psNum);
                if (cljFunc != null) {

                    // 参数
                    List<CljFuncParam> cljFuncParamList = cljFunc.getFuncParams();
                    // 函数体
                    CljCell cljCellBody = cljFunc.getFuncBody();
                    Hashtable<String, CljCell> ht = getParams(cljFuncParamList, lst.subList(1, lst.size()));
                    cljRecur = new CljRecur();
                    cljRecur.setFuncCellBody(cljCellBody);
                    cljRecur.setCljFuncParamList(cljFuncParamList);
                    return MyClojureInterp.eval(cljCellBody, ht, cljRecur, rs);
                }
            } else {
                CljFunc cljFunc = myCljFunc.getCljFunc();
                // 参数
                List<CljFuncParam> cljFuncParamList = cljFunc.getFuncParams();
                // 函数体
                CljCell cljCellBody = cljFunc.getFuncBody();
                Hashtable<String, CljCell> ht = getParams(cljFuncParamList, lst.subList(1, lst.size()));
                cljRecur = new CljRecur();
                cljRecur.setFuncCellBody(cljCellBody);
                cljRecur.setCljFuncParamList(cljFuncParamList);
                return MyClojureInterp.eval(cljCellBody, ht, cljRecur, rs);
            }
        }

        return null;
    }

    /**
     * 匿名调用方法
     * */
    public static CljCell Clj_list_fn_anonymous(List<CljCell> lst, Hashtable<String, CljCell> funcHs, CljRecur cljRecur, CljCell rs) throws Exception {
        if (lst.get(0).getCljCellType() == CljCellType.anonymous) {
            lst.get(0).setCljCellType(CljCellType.Clj_list);

            if (lst.size() > 2) {
                List<CljFuncParam> cljFuncParamList = new ArrayList<>();
                for (int i = 1; i < lst.size(); i++) {
                    funcHs.put(String.valueOf(i), lst.get(i));
                    cljFuncParamList.add(new CljFuncParam(String.valueOf(i)));
                }

                cljRecur = new CljRecur();
                cljRecur.setFuncCellBody(lst.get(0));
                cljRecur.setCljFuncParamList(cljFuncParamList);
                return MyClojureInterp.eval(lst.get(0), funcHs, cljRecur, rs);
            } else if (lst.size() == 2) {
                List<CljFuncParam> cljFuncParamList = new ArrayList<>();
                for (int i = 1; i < lst.size(); i++) {
                    funcHs.put(String.valueOf(i), lst.get(i));
                    cljFuncParamList.add(new CljFuncParam(String.valueOf(i)));
                }

                cljRecur = new CljRecur();
                cljRecur.setFuncCellBody(lst.get(0));
                cljRecur.setCljFuncParamList(cljFuncParamList);
                return MyClojureInterp.eval(lst.get(0), funcHs, cljRecur, rs);
            }
        }

        return null;
    }

    /**
     * apply 计算
     * */
    public static CljCell Clj_list_apply(List<CljCell> lst, Hashtable<String, CljCell> funcHs, CljRecur cljRecur, CljCell rs) throws Exception {
        // apply 计算
        List<CljCell> applyLst = lst.subList(1, lst.size());

        List<CljCell> applyLstNew = new ArrayList<>();
        applyLstNew.add(applyLst.get(0));

        CljCell appCell = new CljCell();
        appCell.setCljCellType(CljCellType.Clj_list);

        if (applyLst.get(1).getCljCellType() == CljCellType.list || applyLst.get(1).getCljCellType() == CljCellType.bracket) {
            for (CljCell cljCell : (List<CljCell>)applyLst.get(1).getCell()) {
                applyLstNew.add(cljCell);
            }
        } else if (applyLst.get(1).getCljCellType() == CljCellType.symbol || applyLst.get(1).getCljCellType() == CljCellType.percent) {
            CljCell ps = funcHs.get(applyLst.get(1).getCell().toString());
            if (ps.getCljCellType() == CljCellType.list || ps.getCljCellType() == CljCellType.set || ps.getCljCellType() == CljCellType.bracket) {
                for (CljCell cljResult : (List<CljCell>)ps.getCell()) {
                    applyLstNew.add(cljResult);
                }
            } else if (ps.getCljCellType() == CljCellType.hash_table) {

            }
        } else if (applyLst.get(1).getCljCellType() == CljCellType.braces) {

        }
        appCell.setCell(applyLstNew);
        return MyClojureInterp.eval(appCell, funcHs, cljRecur, rs);
    }

}














































